import { each } from "@antv/util";
const dashArray = [
  [0, 1],
  [0, 2],
  [1, 2],
  [0, 1, 1, 2],
  [0, 2, 1, 2],
  [1, 2, 1, 2],
  [2, 2, 1, 2],
  [3, 2, 1, 2],
  [4, 2, 1, 2]
];
const interval = 9;
const lineDash = [4, 2, 1, 2];

export default G6 => {

  G6.registerNode('anchor-node', {

    anchorShapes: [],
    dragLog: [],
    anchorNodeId: null,
    stateValue: null,
    setState(name, value, item) {
      const group = item.getContainer();
      if (group.get('destroyed')) return;
      if (group.get('children')) {

        const { anchorControls } = group.get('children')[0].cfg.attrs;
        if (anchorControls && anchorControls.hide) return false;

      }
      this.stateValue = value;
      switch (name) {

        case 'selected':
          this.selected(item.getModel(), group)
          this.reselected(item.getModel(), group);
          break;

        case 'anchor':
          this.drawAnchor(item.getModel(), group);
          this.clearAnchor(group);
          break;

        case 'active_anchor':
          this.activeAnchor(item.getModel(), group);
          this.clearAnchor(group);
          break;

        default:
          break;
      }

    },
    selected(cfg, group) {
      if (!this.stateValue) return;
      let totalArray = [];
      let index = 0;
      const shape = group.getFirst();

      function fn(ratio) {
        for (let i = 0; i < 9; i += interval)  totalArray = totalArray.concat(lineDash);
        const cfg = { lineWidth: 4, stroke: '#1890FF', lineDash: dashArray[index].concat(totalArray) };
        index = (index + 1) % interval;
        return cfg;
      }

      shape.animate(fn, { repeat: true, duration: 5000 });
    },

    reselected(cfg, group) {
      if (this.stateValue) return;
      const shape = group.getFirst();
      shape.stopAnimate();
      shape.attr({ lineWidth: 0, lineDash: null });
    },

    drawAnchor(cfg, group) {
      if (!this.stateValue) return;
      const { type, direction, anchorPointStyles } = group.getFirst().attr();
      const item = group.get('children')[0];
      const bBox = item.getBBox();
      const anchors = this.getAnchorPoints(cfg, group);

      // 绘制锚点坐标
      anchors && anchors.forEach((pos, i) => {
        const diff = type === 'triangle-node' ? (direction === 'up' ? 1 : 0) : 0.5;
        const x = bBox.width * (pos[0] - 0.5);
        const y = bBox.height * (pos[1] - diff);

        /**
         * 绘制三层锚点
         * 最底层: 锚点bg
         * 中间层: 锚点
         * 最顶层: 锚点group, 用于事件触发
         */
        // 视觉锚点
        const anchor = group.addShape('circle', {
          attrs: { x, y, ...anchorPointStyles, r: 4 },
          zIndex: 1,
          nodeId: group.get('id'),
          name: 'anchor',
          draggable: true,
          isAnchor: true,
          index: i,
        });

        // 锚点事件触发的元素
        const anchorGroup = group.addShape('circle', {
          attrs: { x, y, r: 11, fill: '#000', opacity: 0 },
          zIndex: 2,
          nodeId: group.get('id'),
          name: 'anchor-group',
          draggable: true,
          isAnchor: true,
          index: i,
        });

        /**
         * 添加锚点事件绑定
         */

        this.mouseenter(anchorGroup);
        this.mousedown(anchorGroup, group);
        this.dragenter(anchorGroup);
        this.dragstart(anchorGroup, group, pos);
        this.drag(anchorGroup, group);
        this.dragend(anchorGroup, group);
        this.dragleave(anchorGroup);

        this.anchorShapes.push(anchor);
        this.anchorShapes.push(anchorGroup);
      });

    },

    clearAnchor(group) {
      if (this.stateValue) return;
      if (!this.anchorShapes.length) return;
      const line = group.find(e => e.get('name') === 'dashed-line');

      line && line.remove();
      each(this.anchorShapes, anchor => anchor.remove());
      this.anchorShapes = [];
    },

    mouseenter(anchor) {
      anchor.on('mouseenter', anchor.attr.bind(anchor, { cursor: 'crosshair' }));
    },

    mousedown(anchor, group) {
      anchor.on('mousedown', () => this.setState.call(this, 'selected', false, group.get('item')));
    },

    dragenter(anchor) {
      const { anchorNodeId, anchorShapes } = this;
      anchor.on('dragenter', e => {
        if (e.target.cfg.nodeId !== anchorNodeId) {// 排除相同节点的锚点
          const { index } = e.target.cfg;
          const shapes = anchorShapes.filter(c => c.get('name') === 'node-anchor-bg');
          if (shapes[index]) {
            shapes[index].attr('fillOpacity', 0.7);
          }
        }
      });
    },

    dragstart(anchor, group, pos) {
      anchor.on('dragstart', e => {
        const { type, direction } = group.getFirst().attr();
        const diff = type === 'triangle-node' ? (direction === 'up' ? 1 : 0) : 0.5;
        const bbox = group.get('item').getBBox();
        const id = group.get('item').get('id');
        const point = [
          bbox.width * (pos[0] - 0.5), // x
          bbox.height * (pos[1] - diff), // y
        ];

        this.dragLog = [e.x, e.y];

        // 添加线条
        const line = group.addShape('path', {
          name: 'dashed-line',
          pointStart: point,
          attrs: {
            stroke: '#1890FF',
            lineDash: [5, 5],
            zIndex: 9,
            path: [
              ['M', ...point],
              ['L', ...point],
            ],
          },
        });

        group.toFront();// 置于顶层
        line.toFront(); // 最后把这条线层级提升至最高
        this.anchorNodeId = id;
      });
    },

    drag(anchor, group) {
      anchor.on('drag', e => {
        const { dragLog } = this;
        const line = group.find(item => item.get('name') === 'dashed-line');
        const { type, direction } = group.getFirst().attr();
        const canvasBox = group.get('children')[0].get('canvasBBox');

        if (!canvasBox || !line) return;
        const diff = type === 'triangle-node' ? (direction === 'up' ? canvasBox.height : 0) : canvasBox.height / 2;
        const pointStart = line.get('pointStart');
        const endPoint = [e.x - canvasBox.x - canvasBox.width / 2, e.y - canvasBox.y - diff];

        line.toFront();
        /**
         * 计算方法:
         * 鼠标位置 - box左上角 - width/2 => 中心坐标
         * 这里 1px 是为了让鼠标释放时 node: drag 事件监听到 target, 而不是当前虚线
         */

        // 如果鼠标移动距离超过 10px 就开始计算角度
        if (Math.sqrt(Math.pow(Math.abs(dragLog[0]) - Math.abs(e.x), 2) + Math.pow(Math.abs(dragLog[1]) - Math.abs(e.y), 2)) >= 10) {
          if (e.x >= dragLog[0]) {
            // 右下
            if (e.y >= dragLog[1]) {
              endPoint[0] -= 1;
              endPoint[1] -= 1;
            } else {
              // 右上
              endPoint[0] -= 1;
              endPoint[1] -= 1;
            }
          } else {
            // 左上
            if (e.y >= dragLog[1]) {
              endPoint[0] += 1;
              endPoint[1] += 1;
            } else {
              // 左下
              endPoint[0] += 1;
              endPoint[1] += 1;
            }
          }
        }

        line.attr({
          path: [
            ['M', ...pointStart],
            ['L', endPoint[0], endPoint[1]],
          ],
        });
      });
    },

    dragend(anchor, group) {
      anchor.on('dragend', e => {
        const item = group.find(item => item.get('name') === 'dashed-line');
        item.remove();
        this.anchorNodeId = null;
      });
    },

    dragleave(anchor) {
      const { anchorNodeId, anchorShapes } = this;
      anchor.on('dragleave', e => {
        if (e.target.cfg.nodeId !== anchorNodeId) { // 排除相同节点的锚点
          const { index } = e.target.cfg;
          const shapes = anchorShapes.filter(c => c.get('name') === 'node-anchor-bg');

          if (shapes[index]) {
            shapes[index].attr('fillOpacity', 0.5);
          }

        }
      });
    },

    activeAnchor(cfg, group) {
      if (!this.stateValue) return;
      const { anchorShapes } = this;

      if (group.get('children')) {
        const { anchorControls } = group.get('children')[0].cfg.attrs;
        if (anchorControls && anchorControls.hide) return false;
      }

      const model = group.get('item').getModel();
      const { anchorPoints, anchorHotsoptStyles } = model;

      this.drawAnchor(cfg, group);

      const points = this.getAnchorPoints({ anchorPoints }, group);

      points.forEach((pos, i) => {
        const bbox = group.get('children')[0].getBBox();
        const hotspot = group.addShape('circle', {// 激活元素
          zIndex: 0,
          attrs: {
            x: bbox.minX + bbox.width * pos[0],
            y: bbox.minY + bbox.height * pos[1],
            r: 0,
            opacity: 0.5,
            fill: '#1890ff',
            ...anchorHotsoptStyles,
          },
          nodeId: group.get('item').get('id'),
          name: 'node-anchor-bg',
          draggable: true,
          isAnchor: true,
          index: i,
        });

        // 锚点动画
        hotspot.animate({ r: 12 }, { duration: 200 });

        group.sort(); // 将group中的元素按照 index 从大到小排序
        anchorShapes.push(hotspot);
      });

      anchorShapes.forEach(item => {
        if (item.get('name') === 'anchor') {
          item.toFront();
        }
        if (item.get('name') === 'anchor-group') {
          item.attr({ r: (anchorHotsoptStyles && anchorHotsoptStyles.r || 11) + 2, });
          item.toFront();
        }
      });

    },

    getAnchorPoints(cfg) {
      return cfg.anchorPoints || [
        [0.5, 0],
        [1, 0.5],
        [0.5, 1],
        [0, 0.5],
      ];
    },
  }, 'single-node');

}